﻿Imports System.IO
Imports Microsoft.ML
Imports Microsoft.ML.Data

Public Class Tera計算EIQAI
＃Region "AI予測計算予測結果Dgv表示"
    ' ========================================
    ' クラスレベル変数
    ' ========================================
    Private mlContext As MLContext
    Private trainedModels As Dictionary(Of String, ITransformer)
    Private modelFolder As String
    Private 出力フィールド名リスト As List(Of String)

    ' データ構造定義
    Public Class ModelData
        <LoadColumn(0)> Public モデル連番 As Single
        <LoadColumn(1)> Public 出荷先数 As Single
        <LoadColumn(2)> Public アイテム数 As Single
        <LoadColumn(3)> Public バラ数量 As Single
        <LoadColumn(4)> Public 行数 As Single
        <LoadColumn(5)> Public E1 As Single
        <LoadColumn(6)> Public E2 As Single
        <LoadColumn(7)> Public E3 As Single
        <LoadColumn(8)> Public E4 As Single
        <LoadColumn(9)> Public E5 As Single
        <LoadColumn(10)> Public I1 As Single
        <LoadColumn(11)> Public I2 As Single
        <LoadColumn(12)> Public I3 As Single
        <LoadColumn(13)> Public I4 As Single
        <LoadColumn(14)> Public I5 As Single
        ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25も同様に定義
        ' (省略：必要に応じて追加)
    End Class

    ' 入力データ構造
    Public Class InputData
        Public 出荷先数 As Single
        Public アイテム数 As Single
    End Class

    ' 単一予測結果クラス
    Public Class SinglePrediction
        <ColumnName("Score")>
        Public Score As Single
    End Class

    ' ========================================
    ' フォームロード
    ' ========================================
    Private Sub Tera計算EIQAI_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ファイル確認()

        Try
            ' MLContextを初期化
            mlContext = New MLContext(seed:=0)
            modelFolder = モデル保存Path
            trainedModels = New Dictionary(Of String, ITransformer)

            ' モデルフォルダの確認
            If Not Directory.Exists(modelFolder) Then
                MsgBox("学習済モデルフォルダが見つかりません。" & vbCrLf &
                      $"フォルダ: {modelFolder}" & vbCrLf & vbCrLf &
                      "モデルファイル(.zip)を上記フォルダに配置してください。",
                      MsgBoxStyle.Exclamation, "フォルダ未検出")
            End If

            ' 出力フィールド名リストを初期化
            出力フィールド名リスト初期化()

            ' 自動的にモデルを読み込む
            学習済モデル読込()

            ' AI予測EIQマトリクス表Dgvのヘッダー初期化
            AI予測EIQマトリクス表Dgv初期化()

        Catch ex As Exception
            MsgBox($"初期化エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub

    ' ========================================
    ' 出力フィールド名リストの初期化
    ' ========================================
    Private Sub 出力フィールド名リスト初期化()
        出力フィールド名リスト = New List(Of String) From {
            "バラ数量", "行数",
            "E1", "E2", "E3", "E4", "E5",
            "I1", "I2", "I3", "I4", "I5"
        }

        ' 行数01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"行数{i:D2}")
        Next

        ' バラ数01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"バラ数{i:D2}")
        Next

        ' ケース01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"ケース{i:D2}")
        Next

        ' PL01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"PL{i:D2}")
        Next

        ' 容積01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"容積{i:D2}")
        Next

        ' 重量01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"重量{i:D2}")
        Next
    End Sub

    ' ========================================
    ' UI初期化 (Designer.vbで管理するためコメントアウト)
    ' ========================================
    'Private Sub InitializeUI()
    '    ' グループボックス1: 入力
    '    Dim gb1 As New GroupBox With {
    '        .Text = "予測条件入力",
    '        .Location = New Point(10, 10),
    '        .Size = New Size(380, 150)
    '    }
    '
    '    Dim lb1 As New Label With {
    '        .Text = "出荷先数:",
    '        .Location = New Point(20, 40),
    '        .Size = New Size(100, 20),
    '        .Font = New Font("MS UI Gothic", 10, FontStyle.Bold)
    '    }
    '    gb1.Controls.Add(lb1)
    '
    '    出荷先数Tb = New TextBox With {
    '        .Location = New Point(130, 37),
    '        .Size = New Size(120, 25),
    '        .Font = New Font("MS UI Gothic", 12)
    '    }
    '    gb1.Controls.Add(出荷先数Tb)
    '
    '    Dim lb2 As New Label With {
    '        .Text = "アイテム数:",
    '        .Location = New Point(20, 80),
    '        .Size = New Size(100, 20),
    '        .Font = New Font("MS UI Gothic", 10, FontStyle.Bold)
    '    }
    '    gb1.Controls.Add(lb2)
    '
    '    アイテム数Tb = New TextBox With {
    '        .Location = New Point(130, 77),
    '        .Size = New Size(120, 25),
    '        .Font = New Font("MS UI Gothic", 12)
    '    }
    '    gb1.Controls.Add(アイテム数Tb)
    '
    '    予測実行Bt = New Button With {
    '        .Text = "予測 実行",
    '        .Location = New Point(20, 110),
    '        .Size = New Size(230, 35),
    '        .Font = New Font("MS UI Gothic", 11, FontStyle.Bold),
    '        .BackColor = Color.LightGreen
    '    }
    '    gb1.Controls.Add(予測実行Bt)
    '
    '    Me.Controls.Add(gb1)
    '
    '    ' グループボックス2: 操作
    '    Dim gb2 As New GroupBox With {
    '        .Text = "操作",
    '        .Location = New Point(400, 10),
    '        .Size = New Size(370, 150)
    '    }
    '
    '    学習済モデル読込Bt = New Button With {
    '        .Text = "学習済モデル 再読込",
    '        .Location = New Point(20, 30),
    '        .Size = New Size(160, 35),
    '        .Font = New Font("MS UI Gothic", 10)
    '    }
    '    gb2.Controls.Add(学習済モデル読込Bt)
    '
    '    結果エクスポートBt = New Button With {
    '        .Text = "結果をCSVに出力",
    '        .Location = New Point(190, 30),
    '        .Size = New Size(160, 35),
    '        .Font = New Font("MS UI Gothic", 10)
    '    }
    '    gb2.Controls.Add(結果エクスポートBt)
    '
    '    モデル状態Lb = New Label With {
    '        .Text = "モデル: 未読込",
    '        .Location = New Point(20, 80),
    '        .Size = New Size(330, 20),
    '        .Font = New Font("MS UI Gothic", 10),
    '        .ForeColor = Color.Blue
    '    }
    '    gb2.Controls.Add(モデル状態Lb)
    '
    '    予測状態Lb = New Label With {
    '        .Text = "予測: 未実行",
    '        .Location = New Point(20, 110),
    '        .Size = New Size(330, 20),
    '        .Font = New Font("MS UI Gothic", 10),
    '        .ForeColor = Color.Gray
    '    }
    '    gb2.Controls.Add(予測状態Lb)
    '
    '    Me.Controls.Add(gb2)
    '
    '    ' DataGridView: 予測結果表示
    '    Dim gb3 As New GroupBox With {
    '        .Text = "予測結果",
    '        .Location = New Point(10, 170),
    '        .Size = New Size(760, 380)
    '    }
    '
    '    予測結果Dgv = New DataGridView With {
    '        .Location = New Point(10, 25),
    '        .Size = New Size(740, 345),
    '        .ReadOnly = True,
    '        .AllowUserToAddRows = False,
    '        .AllowUserToDeleteRows = False,
    '        .SelectionMode = DataGridViewSelectionMode.FullRowSelect,
    '        .Font = New Font("MS UI Gothic", 9)
    '    }
    '    gb3.Controls.Add(予測結果Dgv)
    '
    '    Me.Controls.Add(gb3)
    'End Sub

    ' ========================================
    ' 学習済モデル読込
    ' ========================================
    Private Sub 学習済モデル読込()
        Try
            trainedModels.Clear()

            If Not Directory.Exists(modelFolder) Then
                モデル状態Lb.Text = "モデル: フォルダが見つかりません"
                モデル状態Lb.ForeColor = Color.Red
                Exit Sub
            End If

            ' モデルフォルダ内のすべての.zipファイルを読み込む
            Dim modelFiles = Directory.GetFiles(modelFolder, "*.zip")

            If modelFiles.Length = 0 Then
                モデル状態Lb.Text = "モデル: ファイルが見つかりません"
                モデル状態Lb.ForeColor = Color.Red
                Exit Sub
            End If

            For Each modelPath As String In modelFiles
                Try
                    Dim model = mlContext.Model.Load(modelPath, Nothing)
                    Dim fieldName As String = Path.GetFileNameWithoutExtension(modelPath)
                    trainedModels(fieldName) = model
                Catch ex As Exception
                    ' 読み込めないモデルはスキップ
                    Continue For
                End Try
            Next

            モデル状態Lb.Text = $"モデル: {trainedModels.Count} 個読込済"
            モデル状態Lb.ForeColor = Color.Green

        Catch ex As Exception
            モデル状態Lb.Text = "モデル: 読込エラー"
            モデル状態Lb.ForeColor = Color.Red
            MsgBox($"モデル読込エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub

    ' ========================================
    ' 学習済モデル読込ボタン
    ' ========================================
    Private Sub 学習済モデル読込Bt_Click(sender As Object, e As EventArgs) Handles 学習済モデル読込Bt.Click
        学習済モデル読込()

        If trainedModels.Count > 0 Then
            MsgBox($"{trainedModels.Count} 個のモデルを読み込みました。",
                  MsgBoxStyle.Information, "読込完了")
        End If
    End Sub

    ' ========================================
    ' 予測実行ボタン
    ' ========================================
    Private Sub 予測実行Bt_Click(sender As Object, e As EventArgs) Handles 予測実行Bt.Click
        予測実行()
        AI予測EIQマトリクス表（）
    End Sub
    Private Sub 予測実行()

        Try
            ' モデルが読み込まれているか確認
            If trainedModels Is Nothing OrElse trainedModels.Count = 0 Then
                MsgBox("モデルが読み込まれていません。" & vbCrLf &
                      "学習済モデルを配置してください。",
                      MsgBoxStyle.Exclamation, "モデル未読込")
                Exit Sub
            End If

            ' 入力値の検証
            Dim 出荷先数値 As Single
            Dim アイテム数値 As Single

            If Not Single.TryParse(出荷先数Tb.Text, 出荷先数値) Then
                MsgBox("出荷先数に有効な数値を入力してください。",
                      MsgBoxStyle.Exclamation, "入力エラー")
                出荷先数Tb.Focus()
                Exit Sub
            End If

            If Not Single.TryParse(アイテム数Tb.Text, アイテム数値) Then
                MsgBox("アイテム数に有効な数値を入力してください。",
                      MsgBoxStyle.Exclamation, "入力エラー")
                アイテム数Tb.Focus()
                Exit Sub
            End If

            ' 入力データを作成
            Dim input As New InputData With {
                .出荷先数 = 出荷先数値,
                .アイテム数 = アイテム数値
            }

            ' 予測状態表示
            予測状態Lb.Text = "予測: 実行中..."
            予測状態Lb.ForeColor = Color.Orange
            Application.DoEvents()

            ' 各モデルで予測を実行
            Dim predictions As New Dictionary(Of String, Single)

            For Each kvp As KeyValuePair(Of String, ITransformer) In trainedModels
                Try
                    Dim fieldName As String = kvp.Key
                    Dim model = kvp.Value

                    ' 予測エンジンを作成（単一出力用）
                    Dim predictionEngine = mlContext.Model.CreatePredictionEngine(Of InputData, SinglePrediction)(model)

                    ' 予測を実行
                    Dim prediction = predictionEngine.Predict(input)

                    ' 結果を保存
                    predictions(fieldName) = prediction.Score

                Catch ex As Exception
                    ' エラーがあればスキップ
                    Continue For
                End Try
            Next

            ' 結果を表示
            予測結果表示(predictions)

            予測状態Lb.Text = $"予測: 完了 ({predictions.Count} 項目)"
            予測状態Lb.ForeColor = Color.Green

            MsgBox($"予測が完了しました。{vbCrLf}{predictions.Count} 項目の予測値を計算しました。",
                  MsgBoxStyle.Information, "予測完了")

        Catch ex As Exception
            予測状態Lb.Text = "予測: エラー"
            予測状態Lb.ForeColor = Color.Red
            MsgBox($"予測エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub

    ' ========================================
    ' 予測結果を表示
    ' ========================================
    Private Sub 予測結果表示(predictions As Dictionary(Of String, Single))
        ' DataGridViewに結果を表示
        Dim dt As New DataTable()
        dt.Columns.Add("項目", GetType(String))
        dt.Columns.Add("予測値", GetType(String))

        ' 出力フィールド名リストの順序に従って予測結果を表示
        For Each fieldName As String In 出力フィールド名リスト
            If predictions.ContainsKey(fieldName) Then
                dt.Rows.Add(fieldName, predictions(fieldName).ToString("F2"))
            End If
        Next

        ' リストにないフィールドも追加（念のため）
        For Each kvp As KeyValuePair(Of String, Single) In predictions
            If Not 出力フィールド名リスト.Contains(kvp.Key) Then
                dt.Rows.Add(kvp.Key, kvp.Value.ToString("F2"))
            End If
        Next

        予測結果Dgv.DataSource = dt
        予測結果Dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
    End Sub

    ' ========================================
    ' 結果をCSVにエクスポート
    ' ========================================
    Private Sub 結果エクスポートBt_Click(sender As Object, e As EventArgs) Handles 結果エクスポートBt.Click
        Try
            If 予測結果Dgv.DataSource Is Nothing Then
                MsgBox("エクスポートする予測結果がありません。",
                      MsgBoxStyle.Exclamation, "データなし")
                Exit Sub
            End If

            ' ファイル名を生成（入力条件を含めない簡潔版）
            Dim fileName As String = $"予測結果_{DateTime.Now:yyyyMMdd_HHmmss}.csv"
            Dim exportPath As String = Path.Combine(計算結果Path, fileName)

            ' フォルダが存在しない場合は作成
            Dim exportFolder As String = Path.GetDirectoryName(exportPath)
            If Not Directory.Exists(exportFolder) Then
                Directory.CreateDirectory(exportFolder)
            End If

            ' DataGridViewの内容をCSVに出力（入力条件も含める）
            Dim dt = CType(予測結果Dgv.DataSource, DataTable)

            Using writer As New StreamWriter(exportPath, False, System.Text.Encoding.UTF8)
                ' 入力条件をヘッダーとして出力
                writer.WriteLine($"# 出荷先数,{出荷先数Tb.Text}")
                writer.WriteLine($"# アイテム数,{アイテム数Tb.Text}")
                writer.WriteLine("# ---,")

                ' ヘッダー行（DataGridViewの列名と同じ）
                writer.WriteLine("項目,予測値")

                ' データ行（DataGridViewの内容と同じ）
                For Each row As DataRow In dt.Rows
                    writer.WriteLine($"{row(0)},{row(1)}")
                Next
            End Using

            MsgBox($"予測結果をエクスポートしました。{vbCrLf}保存先: {exportPath}",
                  MsgBoxStyle.Information, "エクスポート完了")

        Catch ex As Exception
            MsgBox($"エクスポートエラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub


#End Region


＃Region "AI予測EIQマトリクス表作成"
    Private Sub AI予測EIQマトリクス表（)
        Dim i, j, n As Integer


        If 抽出_出庫回数Rb.Checked = True Then n = 13
        If 抽出_バラRb.Checked = True Then n = 38
        If 抽出_ケースRb.Checked = True Then n = 63
        If 抽出_PLRb.Checked = True Then n = 88
        If 抽出_容積Rb.Checked = True Then n = 113
        If 抽出_重量Rb.Checked = True Then n = 138


        Try
            ' DataGridViewに少なくとも1行1列あるか確認してから取得
            If 予測結果Dgv.Rows IsNot Nothing AndAlso 予測結果Dgv.Rows.Count > 0 AndAlso 予測結果Dgv.Columns.Count > 0 Then
                Dim val = 予測結果Dgv.Rows(0).Cells(0).Value
                抽出I_A1Tb.Text = If(val Is Nothing, String.Empty, val.ToString())
            Else
                抽出I_A1Tb.Text = String.Empty
            End If
        Catch ex As Exception
            ' 予期せぬエラー時は空文字を設定（必要ならログ等を追加）
            抽出I_A1Tb.Text = String.Empty
        End Try
    End Sub

    ' ========================================
    ' AI予測EIQマトリクス表Dgvのヘッダー初期化
    ' ========================================
    Private Sub AI予測EIQマトリクス表Dgv初期化()
        ' マトリクス表の列ヘッダーを設定
        AI予測EIQマトリクス表Dgv.Columns.Clear()
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLEランク", "GPLEランク")
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLI_B", "GPLI_B")
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLI_C", "GPLI_C")
        AI予測EIQマトリクス表Dgv.Columns.Add("GPLI_D", "GPLI_D")

        ' 列幅を調整
        AI予測EIQマトリクス表Dgv.Columns("GPLEランク").Width = 120
        AI予測EIQマトリクス表Dgv.Columns("GPLI_A1").Width = 100
        AI予測EIQマトリクス表Dgv.Columns("GPLI_A2").Width = 100
        AI予測EIQマトリクス表Dgv.Columns("GPLI_B").Width = 100
        AI予測EIQマトリクス表Dgv.Columns("GPLI_C").Width = 100
        AI予測EIQマトリクス表Dgv.Columns("GPLI_D").Width = 100

        ' 0列( GPLEランク ) の0行〜4行にラベルを設定
        Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
        AI予測EIQマトリクス表Dgv.Rows.Clear()
        For Each lbl As String In labels
            AI予測EIQマトリクス表Dgv.Rows.Add(New Object() {lbl, String.Empty, String.Empty, String.Empty, String.Empty, String.Empty})
        Next

        ' 読み取り専用にする
        AI予測EIQマトリクス表Dgv.ReadOnly = True
    End Sub

    ' ========================================
    ' GPLI_A1..GPLI_D のヘッダーを設定
    ' ========================================
    Private Sub GPLI_A1_D_ヘッダー設定()
        ' GPLI_A1..D列のヘッダーを設定
        予測結果Dgv.Columns("GPLI_A1").HeaderText = "ヘッダーA1"
        予測結果Dgv.Columns("GPLI_A2").HeaderText = "ヘッダーA2"
        予測結果Dgv.Columns("GPLI_A3").HeaderText = "ヘッダーA3"
        予測結果Dgv.Columns("GPLI_A4").HeaderText = "ヘッダーA4"
        予測結果Dgv.Columns("GPLI_A5").HeaderText = "ヘッダーA5"
        予測結果Dgv.Columns("GPLI_B1").HeaderText = "ヘッダーB1"
        予測結果Dgv.Columns("GPLI_B2").HeaderText = "ヘッダーB2"
        予測結果Dgv.Columns("GPLI_B3").HeaderText = "ヘッダーB3"
        予測結果Dgv.Columns("GPLI_B4").HeaderText = "ヘッダーB4"
        予測結果Dgv.Columns("GPLI_B5").HeaderText = "ヘッダーB5"
        予測結果Dgv.Columns("GPLI_C1").HeaderText = "ヘッダーC1"
        予測結果Dgv.Columns("GPLI_C2").HeaderText = "ヘッダーC2"
        予測結果Dgv.Columns("GPLI_C3").HeaderText = "ヘッダーC3"
        予測結果Dgv.Columns("GPLI_C4").HeaderText = "ヘッダーC4"
        予測結果Dgv.Columns("GPLI_C5").HeaderText = "ヘッダーC5"
        予測結果Dgv.Columns("GPLI_D1").HeaderText = "ヘッダーD1"
        予測結果Dgv.Columns("GPLI_D2").HeaderText = "ヘッダーD2"
        予測結果Dgv.Columns("GPLI_D3").HeaderText = "ヘッダーD3"
        予測結果Dgv.Columns("GPLI_D4").HeaderText = "ヘッダーD4"
        予測結果Dgv.Columns("GPLI_D5").HeaderText = "ヘッダーD5"
    End Sub

#End Region

End Class
